/*
 * Node.cpp
 *
 *  Created on: 12-05-2013
 *      Author: Jarek
 */

#include "ENode.h"
#include "EWorld.h"
#include "EMesh.h"
#include "EModel.h"
#include "ELight.h"
#include "ECamera.h"

Node::Node() :
	name					(NULL),
	lastWorldMatrixTime		(-1.0f),
	anim					(NULL),
	parent					(NULL),
	children				(NULL),
	childrenCount			(0)
{
	nodeType				= NODE_TYPE_UNKNOWN;
}

Node::~Node()
{
	DELETE_ARRAY(name);
	DELETE(anim);
}

char* Node::getName() const
{
	return name;
}

void Node::setName(const char* name)
{
	if(name == NULL)
		return;

	DELETE_ARRAY(this->name);
	this->name = new char[strlen(name) + 1];
	strcpy(this->name, name);
}

/**
 * "Constructor" for a node. It is called by it's deriving classes to process their children as well.
 */
void Node::parseData(const World& scene, const aiNode& ainode, const Node* parent)
{
	// Set matrix, name and parent.
	this->setWorldMatrix(ainode.mTransformation);
	this->setName(ainode.mName.data);
	this->parent = const_cast<Node*>(parent);

	// Parse children.
	childrenCount = ainode.mNumChildren;
	if (childrenCount > 0)
	{
		children = new Node*[childrenCount];
		for (uint c = 0; c < childrenCount; ++c)
		{
			const aiNode* currentChild = ainode.mChildren[c];

			if (currentChild->mNumMeshes > 0)
			{
				children[c] = new Model();
				children[c]->parseData(scene, *currentChild, this);
				continue;
			}

			for (uint i = 0; i < scene.lightsCount; ++i)
			{
				if (strcmp(scene.lights[i].getName(), currentChild->mName.data) == 0)
				{
					children[c] = &scene.lights[i];
					children[c]->parseData(scene, *currentChild, this);
					goto SKIP;
				}
			}

			for (uint i = 0; i < scene.camerasCount; ++i)
			{
				if (strcmp(scene.cameras[i].getName(), currentChild->mName.data) == 0)
				{
					children[c] = &scene.cameras[i];
					children[c]->parseData(scene, *currentChild, this);
					goto SKIP;
				}
			}

			LOGE("There is a node \"%s\" in hierarchy, that is not a model, light or camera", currentChild->mName.data);
			children[c] = new Node();
			children[c]->parseData(scene, *currentChild, this);

			SKIP:
				continue;
		}
	}
}

void Node::setWorldMatrix(const aiMatrix4x4& aimatrix)
{
	this->worldMatrix.at(0, 0) = aimatrix.a1;
	this->worldMatrix.at(0, 1) = aimatrix.a2;
	this->worldMatrix.at(0, 2) = aimatrix.a3;
	this->worldMatrix.at(0, 3) = aimatrix.a4;

	this->worldMatrix.at(1, 0) = aimatrix.b1;
	this->worldMatrix.at(1, 1) = aimatrix.b2;
	this->worldMatrix.at(1, 2) = aimatrix.b3;
	this->worldMatrix.at(1, 3) = aimatrix.b4;

	this->worldMatrix.at(2, 0) = aimatrix.c1;
	this->worldMatrix.at(2, 1) = aimatrix.c2;
	this->worldMatrix.at(2, 2) = aimatrix.c3;
	this->worldMatrix.at(2, 3) = aimatrix.c4;

	this->worldMatrix.at(3, 0) = aimatrix.d1;
	this->worldMatrix.at(3, 1) = aimatrix.d2;
	this->worldMatrix.at(3, 2) = aimatrix.d3;
	this->worldMatrix.at(3, 3) = aimatrix.d4;
}

Matrix4x4<float> Node::getWorldMatrix() const
{
	return worldMatrix;
}

Matrix4x4<float> Node::getWorldMatrix(const ETime& time)
{
	if(time.totalTime == lastWorldMatrixTime )
		return lastWorldMatrix;

	lastWorldMatrixTime = time.totalTime;

	if(anim == NULL)
	{
		if(parent == NULL) {
			lastWorldMatrix = worldMatrix;
		}else {
			lastWorldMatrix = parent->getWorldMatrix(time)*worldMatrix;
		}
		return lastWorldMatrix;
	}

	float duration		= anim->endTime - anim->startTime;
	float convertedTime = fmod(time.totalTime, duration);

	if(convertedTime < anim->startTime)
		convertedTime+=duration;

	if(parent == NULL) {
		lastWorldMatrix.setIdentity();
	} else {
		lastWorldMatrix = parent->getWorldMatrix(time);
	}

	Matrix4x4<float> scalingMatrix 		= Matrix4x4<float>::getScalingMatrix(anim->getScalingData(convertedTime));
	Matrix4x4<float> rotationMatrix 	= Matrix4x4<float>::getRotationMatrix(anim->getRotationData(convertedTime));
	Matrix4x4<float> translationMatrix 	= Matrix4x4<float>::getTranslationMatrix(anim->getTranslationData(convertedTime));

	lastWorldMatrix *= translationMatrix * rotationMatrix * scalingMatrix;
	return lastWorldMatrix;
}

bool Node::parseAnimationData(const aiNodeAnim& animation, const float ticksPerSecond)
{
	if (this->name == NULL || strcmp(this->name, animation.mNodeName.data) != 0)
	{
		if (childrenCount == 0)
			return false;
		for (uint i = 0; i < childrenCount; ++i)
		{
			if (children[i]->parseAnimationData(animation, ticksPerSecond))
				return true;
		}
		return false;
	}
	anim 			= new Animation();

	anim->keys.translationKeysCount				= animation.mNumPositionKeys;
	anim->keys.rotationKeysCount			= animation.mNumRotationKeys;
	anim->keys.scalingKeysCount				= animation.mNumScalingKeys;

	anim->keys.translationKeys				= new Animation::Key<Vector3<float> > [anim->keys.translationKeysCount];
	anim->keys.rotationKeys					= new Animation::Key<Quaternion<float> > [anim->keys.rotationKeysCount];
	anim->keys.scalingKeys					= new Animation::Key<Vector3<float> > [anim->keys.scalingKeysCount];

	for (uint i = 0; i < anim->keys.translationKeysCount; i++) {
		anim->keys.translationKeys[i] 	= Animation::Key< Vector3<float> > (static_cast<float>(animation.mPositionKeys[i].mTime) / ticksPerSecond, Vector3<float>(animation.mPositionKeys[i].mValue.x,
												animation.mPositionKeys[i].mValue.y,
												animation.mPositionKeys[i].mValue.z));
	}

	for (uint j = 0; j < anim->keys.rotationKeysCount; j++)
		anim->keys.rotationKeys[j]		= Animation::Key< Quaternion<float> > (static_cast<float>(animation.mRotationKeys[j].mTime) / ticksPerSecond, Quaternion<float>(animation.mRotationKeys[j].mValue.w,
												animation.mRotationKeys[j].mValue.x, animation.mRotationKeys[j].mValue.y,
												animation.mRotationKeys[j].mValue.z));

	for (uint k = 0; k < anim->keys.scalingKeysCount; k++)
		anim->keys.scalingKeys[k] 		= Animation::Key< Vector3<float> > (static_cast<float>(animation.mScalingKeys[k].mTime) / ticksPerSecond,
												Vector3<float>(animation.mScalingKeys[k].mValue.x, animation.mScalingKeys[k].mValue.y,
												animation.mScalingKeys[k].mValue.z));

	anim->prevState					= Animation::mapAnimationState(animation.mPreState);
	anim->postState					= Animation::mapAnimationState(animation.mPostState);

	anim->startTime					= anim->keys.translationKeys[0].time;
	anim->endTime					= anim->keys.translationKeys[anim->keys.translationKeysCount-1].time;

	return true;
}


